home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 051-075 / disk_052 / tek4010 / kermit.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  20KB  |  838 lines

  1. /*************************************************************
  2.  * vt100 terminal emulator - KERMIT protocol support
  3.  *           
  4.  *           860823 DBW - Integrated and rewrote lots of code
  5.  *           860811 Steve Drew multi filexfer, bugs, status line ect..
  6.  *      v2.0 860809 DBW - Major rewrite
  7.  *      v1.1 860720 DBW - Switches, 80 cols, colors, bug fixes
  8.  *      v1.0 860712 DBW - First version released
  9.  *
  10.  *************************************************************/
  11.  
  12. #define MODULE_KERMIT 1
  13. #include "vt100.h"
  14.  
  15. #define MAXPACKSIZ 94       /* Maximum msgpkt size */
  16. #define CR         13       /* ASCII Carriage Return */
  17. #define LF         10       /* ASCII line feed */
  18. #define SP         32       /* ASCII space */
  19. #define DEL       127       /* Delete (rubout) */
  20.  
  21. #define MAXTRY    5        /* Times to retry a msgpkt */
  22. #define MYQUOTE  '#'       /* Quote character I will use */
  23. #define MYRPTQ   '~'       /* Repeat quote character */
  24. #define MYPAD      0       /* Number of padding characters I will need */
  25. #define MYPCHAR    0       /* Padding character I need (NULL) */
  26. #define MYEOL    '\n'      /* End-Of-Line character I need */
  27.  
  28. #define tochar(ch)  ((ch) + ' ')
  29. #define unchar(ch)  ((ch) - ' ')
  30. #define ctl(ch)     ((ch) ^ 64 )
  31.  
  32. /* Global Variables */
  33.  
  34. int
  35.    size,      /* Size of present data */
  36.    osize,     /* Size of last data entry */
  37.    rpsiz,     /* Maximum receive msgpkt size */
  38.    spsiz,     /* Maximum send msgpkt size */
  39.    timint,    /* Time interval to wait */
  40.    pad,       /* How much padding to send */
  41.    n,         /* Packet number */
  42.    tp,        /* total packets */
  43.    numtry,    /* Times this msgpkt retried */
  44.    retry,     /* total retries */
  45.    oldtry,    /* Times previous msgpkt retried */
  46.    rptflg,    /* are we doing repeat quoting */
  47.    first,     /* is this the first time in a file */
  48.    rpt,       /* current repeat count */
  49.    next,      /* what is the next character */
  50.    t;         /* current character */
  51. long
  52.    totbytes;  /* total bytes xfered on this file */
  53.  
  54. char 
  55.    state,     /* Present state of the automaton */
  56.    padchar,   /* Padding character to send */
  57.    eol,       /* End-Of-Line character to send */
  58.    quote,     /* Quote character in incoming data */
  59.    rptq,      /* Quote character for repeats */
  60.    ackpkt[MAXPACKSIZ+20], /* ACK/NAK packet buffer */
  61.    msgpkt[MAXPACKSIZ+20], /* Message Packet buffer */
  62.    filnam[40];            /* remote file name */
  63.    snum[10];
  64.  
  65. void encode(), decode(), rpar(), spar();
  66.    
  67. FILE *fp;     /* file for send/receive */
  68.  
  69. int shutdown = 0;  /* shut down server after all xfers complete */
  70.  
  71. char *
  72. getfname(name)   /* returns pointer to start of file name from file spec */
  73. char *name;
  74.     {
  75.     int l;
  76.  
  77.     l = strlen(name);
  78.     while(l && name[l] != '/' && name[l] != ':') l--;
  79.     if (name[l] == '/' || name[l] == ':') l++;
  80.     return(name += l);
  81.     }
  82.     
  83. doksend(file,more)
  84. char *file;
  85. int more;
  86.    {
  87.    int retval;
  88.  
  89.    ttime = TTIME_LONG;
  90.    if (!strcmp(file,"$")) { saybye(); return(2); }
  91.    if (file[strlen(file)-1] == '$')  { 
  92.              shutdown = 1; 
  93.              file[strlen(file)-1] = '\0'; 
  94.              }
  95.    if ((fp = fopen(file,"r")) == NULL) {
  96.       emits("Cannot open send file\n");
  97.       return FALSE;
  98.       }
  99.    getready(file,more);
  100.    emits("SEND");
  101.    retval  = sendsw();
  102.    if (retval) { x = 600; emits("DONE"); }
  103.    curmode = 0;
  104.    if (shutdown) saybye();
  105.    emits("\n");
  106.    fclose(fp);
  107.    return(retval);
  108.    }
  109.  
  110. dokreceive(file,more)
  111. char *file;
  112. int more;
  113.    {
  114.    int retval;
  115.  
  116.    ttime = TTIME_LONG;
  117.    if (!strcmp(file,"$")) { saybye(); return(2); }
  118.    if (file[strlen(file)-1] == '$')  { 
  119.              shutdown = 1; 
  120.              file[strlen(file)-1] = '\0'; 
  121.              }
  122.    if ((fp = fopen(file,"w")) == NULL) {
  123.       emits("Cannot open file\n");
  124.       return FALSE;
  125.       }
  126.    getready(file,more);
  127.    emits("RECV");
  128.    retval  = recsw();
  129.    if (retval) { x = 600; emits("DONE"); }
  130.    curmode = 0;
  131.    if (shutdown) saybye();
  132.    emits("\n");
  133.    fclose(fp);
  134.    return(retval);
  135.    }
  136.  
  137. getready(file,more)
  138.    char *file;
  139.    int more;
  140.    {
  141.    if (!more) {   
  142.        emits("Remote file name [");
  143.        emits(getfname(file));
  144.        emits("]: ");
  145.        filename(filnam);
  146.        if (filnam[0] == 0) strcpy(filnam,getfname(file));
  147.        emits("Type <ESC> to abort transfer\n");
  148.        }
  149.    else strcpy(filnam,getfname(file));
  150.    eatup();
  151.    tp =  retry =  0; totbytes = 0L;  n =  numtry = 0;
  152.    want_message = FALSE; /* tell readchar no error msgs status bar instead */
  153.    statusline();
  154.    x = 600;
  155.    }
  156.  
  157.  
  158. sendsw()
  159.    {
  160.    char sinit(), sfile(), sdata(), seof(), sbreak();
  161.  
  162.    state = 'S';
  163.    while(TRUE) {
  164.       switch(state) {
  165.          case 'S':   state = sinit();  break;
  166.          case 'F':   state = sfile();  break;
  167.          case 'D':   state = sdata();  break;
  168.          case 'Z':   state = seof();   break;
  169.          case 'B':   state = sbreak(); break;
  170.          case 'C':   return(TRUE);
  171.          case 'A':   x = 600;
  172.                      if (timeout == USERABORT) { sabort();  return(FALSE); }
  173.                      if (timeout == TIMEOUT) emits("TMOUT");
  174.                      else emits("ERROR");
  175.                      return(FALSE);
  176.          default:    return(FALSE);
  177.          }
  178.       }
  179.    }
  180.  
  181. char sinit()
  182.    {
  183.    int num, len;
  184.    
  185.    retry++;
  186.    if (numtry++ > MAXTRY) return('A');
  187.    spar(msgpkt);
  188.  
  189.    spack('S',n,9,msgpkt);
  190.    switch(rpack(&len,&num,ackpkt)) {
  191.       case 'N':  return(state);
  192.       case 'Y':  if (n != num) return(state);
  193.                  rpar(ackpkt);
  194.                  if (eol == 0) eol = '\n';
  195.                  if (quote == 0) quote = '#';
  196.                  numtry = 0;
  197.                  retry--;
  198.                  n = (n+1)%64;
  199.                  return('F');
  200.       case 'E':  return('A');
  201.       case FALSE:if (timeout == USERABORT) state = 'A';
  202.                  return(state);
  203.       default:   return('A');
  204.       }
  205.     }
  206.  
  207. char sfile()
  208.    {
  209.    int num, len;
  210.  
  211.    retry++;
  212.    if (numtry++ > MAXTRY) return('A');
  213.  
  214.    spack('F',n,strlen(filnam),filnam);
  215.    switch(rpack(&len,&num,ackpkt)) {
  216.       case 'N':
  217.          num = (--num<0 ? 63:num);
  218.          if (n != num) return(state);
  219.       case 'Y':
  220.          if (n != num) return(state);
  221.          numtry = 0;
  222.          retry--;
  223.          n = (n+1)%64;
  224.          first = 1;
  225.          size = getpkt();
  226.          return('D');
  227.       case 'E':
  228.          return('A');
  229.       case FALSE: if (timeout == USERABORT) state = 'A';
  230.                   return(state);
  231.       default:    return('A');
  232.       }
  233.    }
  234.  
  235. char sdata()
  236.    {
  237.    int num, len;
  238.    
  239.    retry++;
  240.    if (numtry++ > MAXTRY) return('A');
  241.  
  242.    spack('D',n,size,msgpkt);
  243.    switch(rpack(&len,&num,ackpkt)) {
  244.       case 'N':
  245.          num = (--num<0 ? 63:num);
  246.          if (n != num) return(state);
  247.       case 'Y':
  248.          if (n != num) return(state);
  249.          numtry = 0;
  250.          retry--;
  251.          n = (n+1)%64;
  252.          if ((size = getpkt()) == 0) return('Z');
  253.          return('D');
  254.       case 'E':
  255.          return('A');
  256.       case FALSE: if (timeout == USERABORT) state = 'A';
  257.                   return(state);
  258.       default:    return('A');
  259.       }
  260.    }
  261.  
  262. char seof()
  263.    {
  264.    int num, len;
  265.    retry++;
  266.    if (numtry++ > MAXTRY) return('A');
  267.  
  268.    if (timeout == USERABORT) { 
  269.         timeout = GOODREAD;
  270.         spack('Z',n,1,"D"); /* tell host to discard file */
  271.         }
  272.    else spack('Z',n,0,msgpkt);
  273.    switch(rpack(&len,&num,ackpkt)) {
  274.       case 'N':
  275.          num = (--num<0 ? 63:num);
  276.          if (n != num) return(state);
  277.       case 'Y':
  278.          if (n != num) return(state);
  279.          numtry = 0;
  280.          retry--;
  281.          n = (n+1)%64;
  282.          return('B');
  283.       case 'E':
  284.          return('A');
  285.       case FALSE: return(state);
  286.       default:    return('A');
  287.       }
  288.    }
  289.  
  290. char sbreak()
  291.    {
  292.    int num, len;
  293.    retry++;
  294.    if (numtry++ > MAXTRY) return('A');
  295.  
  296.    spack('B',n,0,msgpkt);
  297.    switch (rpack(&len,&num,ackpkt)) {
  298.       case 'N':
  299.          num = (--num<0 ? 63:num);
  300.          if (n != num) return(state);
  301.       case 'Y':
  302.          if (n != num) return(state);
  303.          numtry = 0;
  304.          retry--;
  305.          n = (n+1)%64;
  306.          return('C');
  307.       case 'E':
  308.          return('A');
  309.       case FALSE: return(state);
  310.       default:    return ('A');
  311.       }
  312.    }
  313.  
  314. /* timeout equals USERABORT so lets end the file and quit  */
  315. /* when host receives 'Z' packet with "D" in data field he */
  316. /* should discard the file.                                */
  317.  
  318. sabort()
  319.    {
  320.    emits("ABORT");
  321.    n = (n+1)%64;
  322.    retry--;
  323.    state = 'Z';
  324.    while (state == 'Z') state = seof();
  325.    while (state == 'B') state = sbreak();
  326.    return(FALSE);
  327.    }
  328.    
  329. recsw()
  330.    {
  331.    char rinit(), rfile(), rdata(), rbreak();
  332.  
  333.    state = 'R';
  334.    
  335.    while(TRUE) {
  336.       switch(state) {
  337.          case 'R':   state = rinit(); break;
  338.          case 'F':   state = rfile(); break;
  339.          case 'D':   state = rdata(); break;
  340.          case 'Z':   state = rbreak(); break;
  341.          case 'C':   return(TRUE);
  342.          case 'A':   x = 600;
  343.                      if (timeout == USERABORT){/* easy way to cleanly abort 
  344.                                                   should really send and ACK
  345.                                                   with "X" in data field and 
  346.                                                   wait for host to abort but 
  347.                                                   not all kermits support
  348.                                                   this feature.           */
  349.                          emits("ABORT");
  350.                          spack('E',n,0,0); /* send an error packet back   */
  351.                      }
  352.                      else if (timeout == TIMEOUT) emits("TMOUT");
  353.                      else emits("ERROR");           
  354.                      return(FALSE);
  355.          }
  356.       }
  357.    }
  358.  
  359. char rinit()
  360.    {
  361.    int len, num;
  362.    retry++;
  363.    if (numtry++ > MAXTRY) return('A');
  364.  
  365.    if (server) spack('R',n,strlen(filnam),filnam);
  366.    else  spack('N',n,0,0);
  367.    switch(rpack(&len,&num,msgpkt)) {
  368.       case 'S':
  369.          rpar(msgpkt);
  370.          spar(msgpkt);
  371.          spack('Y',n,9,msgpkt);
  372.          oldtry = numtry;
  373.          numtry = 0;
  374.          retry--;
  375.          n = (n+1)%64;
  376.          return('F');
  377.       case 'E':
  378.          return('A');
  379.       case FALSE:
  380.          if (timeout == USERABORT) return('A');
  381.          spack('N',n,0,0);
  382.          return(state);
  383.       default:
  384.          return('A');
  385.       }
  386.    }
  387.  
  388. char rfile()
  389.    {
  390.    int num, len;
  391.    retry++;
  392.    if (numtry++ > MAXTRY) return('A');
  393.  
  394.    switch(rpack(&len,&num,msgpkt)) {
  395.       case 'S':
  396.          if (oldtry++ > MAXTRY) return('A');
  397.          if (num == ((n==0) ? 63:n-1)) {
  398.             spar(msgpkt);
  399.             spack('Y',num,9,msgpkt);
  400.             retry--;
  401.             numtry = 0;
  402.             return(state);
  403.             }
  404.          else return('A');
  405.       case 'Z':
  406.          if (oldtry++ > MAXTRY) return('A');
  407.          if (num == ((n==0) ? 63:n-1)) {
  408.             spack('Y',num,0,0);
  409.             numtry = 0;
  410.             retry--;
  411.             return(state);
  412.             }
  413.          else return('A');
  414.       case 'F':
  415.          if (num != n) return('A');
  416.          spack('Y',n,0,0);
  417.          oldtry = numtry;
  418.          numtry = 0;
  419.          retry--;
  420.          n = (n+1)%64;
  421.          return('D');
  422.       case 'B':
  423.          if (num != n) return ('A');
  424.          spack('Y',n,0,0);
  425.          retry--;
  426.          return('C');
  427.       case 'E':
  428.          return('A');
  429.       case FALSE:
  430.          if (timeout == USERABORT) return('A');
  431.          spack('N',n,0,0);
  432.          return(state);
  433.       default:
  434.          return ('A');
  435.       }
  436.    }
  437.  
  438. char rdata()
  439.    {
  440.    int num, len;
  441.    retry++;
  442.    if (numtry++ > MAXTRY) return('A');
  443.  
  444.    switch(rpack(&len,&num,msgpkt)) {
  445.       case 'D':
  446.          if (num != n) {
  447.             if (oldtry++ > MAXTRY) return('A');
  448.             if (num == ((n==0) ? 63:n-1)) {
  449.                spack('Y',num,6,msgpkt);
  450.                retry--;
  451.                numtry = 0;
  452.                return(state);
  453.                }
  454.             else return('A');
  455.             }
  456.          decode();
  457.          spack('Y',n,0,0);
  458.          oldtry = numtry;
  459.          numtry = 0;
  460.          retry--;
  461.          n = (n+1)%64;
  462.          return('D');
  463.       case 'F':
  464.          if (oldtry++ > MAXTRY) return('A');
  465.          if (num == ((n==0) ? 63:n-1)) {
  466.             spack('Y',num,0,0);
  467.             numtry = 0;
  468.             retry--;
  469.             return(state);
  470.             }
  471.          else return('A');
  472.       case 'Z':
  473.          if (num != n) return('A');
  474.          spack('Y',n,0,0);
  475.          n = (n+1)%64;
  476.          retry--;
  477.          return('Z');
  478.       case 'E':
  479.          return('A');
  480.       case FALSE:
  481.          if (timeout == USERABORT) return('A');
  482.          spack('N',n,0,0);
  483.          return(state);
  484.       default:
  485.         return('A');
  486.       }
  487.    }
  488.  
  489. char rbreak()
  490.    { 
  491.    int num, len;
  492.    retry++;
  493.    if (numtry++ > MAXTRY) return('A');
  494.  
  495.    switch(rpack(&len,&num,msgpkt)) {
  496.       case 'B':
  497.          spack('Y',n,0,0);
  498.          return('C');
  499.       case 'Z':
  500.          spack('Y',n,0,0);
  501.          return('Z');
  502.       case 'E':
  503.          return('A');
  504.       case FALSE:
  505.          spack('N',n,0,0);
  506.          return(state);
  507.       default:
  508.         return('A');
  509.       }
  510.    }
  511.  
  512. spack(type,num,len,data)
  513. char type, *data;
  514. int num, len;
  515.    {
  516.    int i;
  517.    char chksum, buffer[100];
  518.    register char *bufp;
  519.    
  520.    dostats(type);
  521.    bufp = buffer;
  522.    for (i=1; i<=pad; i++) sendchar(padchar);
  523.  
  524.    *bufp++ = SOH;
  525.    *bufp++ = tochar(len+3);
  526.    chksum  = tochar(len+3);
  527.    *bufp++ = tochar(num);
  528.    chksum += tochar(num);
  529.    *bufp++ = type;
  530.    chksum += type;
  531.  
  532.    for (i=0; i<len; i++) {
  533.       *bufp++ = data[i];
  534.       chksum += data[i];
  535.       }
  536.    chksum = (((chksum&0300) >> 6)+chksum)&077;
  537.    *bufp++ = tochar(chksum);
  538.    *bufp++ = '\r';
  539.    *bufp++ = '\n';
  540.    *bufp   = 0;
  541.    sendstring(buffer);
  542.    }
  543.  
  544. rpack(len,num,data)
  545. int *len, *num;
  546. char *data;
  547.    {
  548.    int i, done;
  549.    char type, cchksum, rchksum;
  550.    char t = '\0';
  551.  
  552.     do {
  553.        t = readchar();
  554.        if (timeout != GOODREAD) return(FALSE);
  555.        } while (t != SOH);
  556.  
  557.     done = FALSE;
  558.     while (!done) {
  559.        t = readchar();
  560.        if (timeout != GOODREAD) return(FALSE);
  561.        if (t == SOH) continue;
  562.        cchksum = t;
  563.        *len = unchar(t)-3;
  564.        t = readchar();
  565.        if (timeout != GOODREAD) return(FALSE);
  566.        if (t == SOH) continue;
  567.        cchksum = cchksum + t;
  568.        *num = unchar(t);
  569.        t = readchar();
  570.        if (timeout != GOODREAD) return(FALSE);
  571.        if (t == SOH) continue;
  572.        cchksum = cchksum + t;
  573.        type = t;
  574.        for (i=0; i<*len; i++) {
  575.           t = readchar();
  576.           if (timeout != GOODREAD) return(FALSE);
  577.           if (t == SOH) continue;
  578.           cchksum = cchksum + t;
  579.           data[i] = t;
  580.           }
  581.        data[*len] = 0;
  582.        t = readchar();
  583.        if (timeout != GOODREAD) return(FALSE);
  584.        rchksum = unchar(t);
  585.        t = readchar();
  586.        if (timeout != GOODREAD) return(FALSE);
  587.        if (t == SOH) continue;
  588.        done = TRUE;
  589.        }
  590.    dostats(type);
  591.    cchksum = (((cchksum&0300) >> 6)+cchksum)&077;
  592.    if (cchksum != rchksum) return(FALSE);
  593.    return((int)type);
  594.    }
  595.  
  596. getpkt() {
  597.    int i,eof;
  598.  
  599.    static char leftover[10] = { '\0', '\0', '\0', '\0', '\0',
  600.                                 '\0', '\0', '\0', '\0', '\0' };
  601.  
  602.    if (first == 1) {
  603.       first = 0;
  604.       *leftover = '\0';
  605.       t = getc(fp);
  606.       if (t == EOF) {
  607.          first = 1;
  608.          return(size = 0);
  609.          }
  610.       totbytes++;
  611.       }
  612.    else if (first == -1) {
  613.       first = 1;
  614.       return(size = 0);
  615.       }
  616.    for (size = 0; (msgpkt[size] = leftover[size]) != '\0'; size++) ;
  617.    *leftover = '\0';
  618.    rpt = 0;
  619.    eof = 0;
  620.    while (!eof) {
  621.       next = getc(fp);
  622.       if (next == EOF) {
  623.          first = -1;
  624.          eof   =  1;
  625.          }
  626.       else totbytes++;
  627.       osize = size;
  628.       encode(t);
  629.       t = next;
  630.       if (size == spsiz-3) return(size);
  631.       if (size > spsiz-3) {
  632.          for (i = 0; (leftover[i] = msgpkt[osize+i]) != '\0'; i++) ;
  633.          size = osize;
  634.          msgpkt[size] = '\0';
  635.          return(size);
  636.          }
  637.       }
  638.    return(size);
  639.    }
  640.  
  641. void encode(a)
  642. char a;
  643.    {
  644.    int a7,b8;
  645.  
  646.    if (p_mode == 1 && a == '\n') {
  647.       rpt = 0;
  648.       msgpkt[size++] = quote;
  649.       msgpkt[size++] = ctl('\r');
  650.       if (size <= spsiz-3) osize = size;
  651.       msgpkt[size++] = quote;
  652.       msgpkt[size++] = ctl('\n');
  653.       msgpkt[size]   = '\0';
  654.       return;
  655.       }
  656.    if (rptflg) {
  657.       if (a == next && (first == 0)) {
  658.          if (++rpt < 94) return;
  659.          else if (rpt == 94) {
  660.             msgpkt[size++] = rptq;
  661.             msgpkt[size++] = tochar(rpt);
  662.             rpt = 0;
  663.             }
  664.          }
  665.       else if (rpt == 1) {
  666.          rpt = 0;
  667.          encode(a);
  668.          if (size <= spsiz-3) osize = size; 
  669.          rpt = 0;
  670.          encode(a);
  671.          return;
  672.          }
  673.       else if (rpt > 1) {
  674.          msgpkt[size++] = rptq;
  675.          msgpkt[size++] = tochar(++rpt);
  676.          rpt = 0;
  677.          }
  678.       }
  679.    a7 = a & 0177;
  680.    b8 = a & 0200;
  681.    if ((a7 < SP) || (a7==DEL)) {
  682.       msgpkt[size++] = quote;
  683.       a = ctl(a);
  684.       }
  685.    if (a7 == quote) msgpkt[size++] = quote;
  686.    if ((rptflg) && (a7 == rptq)) msgpkt[size++] = quote;
  687.    msgpkt[size++] = a;
  688.    msgpkt[size] = '\0';
  689.    }
  690.  
  691. void decode()
  692.    {
  693.    USHORT  a, a7;
  694.    char *buf;
  695.  
  696.    buf = msgpkt;
  697.    rpt = 0;
  698.    
  699.    while ((a = *buf++) != '\0') {
  700.       if (rptflg) {
  701.          if (a == rptq) {
  702.             rpt = unchar(*buf++);
  703.             a = *buf++;
  704.             }
  705.          }
  706.       if (a == quote) {
  707.          a  = *buf++;
  708.          a7 = a & 0177;
  709.          if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') a = ctl(a);
  710.          }
  711.       if (rpt == 0) rpt = 1;
  712.       if (p_mode == 1 && a == '\r') continue;
  713.       totbytes += rpt;
  714.       for (; rpt > 0; rpt--) putc(a, fp);
  715.       }
  716.    return;
  717.    }
  718.  
  719. void spar(data)
  720. char data[];
  721.    {
  722.    data[0] = tochar(MAXPACKSIZ);
  723.    data[1] = tochar(TTIME_LONG);
  724.    data[2] = tochar(MYPAD);
  725.    data[3] = ctl(MYPCHAR);
  726.    data[4] = tochar(MYEOL);
  727.    data[5] = MYQUOTE;
  728.    data[6] = 'N';
  729.    data[7] = '1';
  730.    data[8] = MYRPTQ;
  731.    data[9] = '\0';
  732.    }
  733.  
  734. void rpar(data)
  735. char data[];
  736.    {
  737.    spsiz   = unchar(data[0]);
  738.    timint  = unchar(data[1]);
  739.    pad     = unchar(data[2]);
  740.    padchar = ctl(data[3]);
  741.    eol     = unchar(data[4]);
  742.    quote   = data[5];
  743.    rptflg  = 0;
  744.    if (data[6] == 0) return;
  745.    if (data[7] == 0) return;
  746.    if (data[8] == 0) return;
  747.    rptq    = data[8];
  748.    rptflg  = ((rptq > 040 && rptq < 0100) || (rptq > 0140 && rptq < 0177));
  749.    }
  750.  
  751. saybye()
  752.   {
  753.   int len,num;
  754.   shutdown = 0;
  755.   spack('G',n,1,"F");  /* shut down server no more files */
  756.   rpack(&len,&num,ackpkt);
  757.   }
  758.  
  759. statusline()
  760.   {
  761.   emits ("\nFile:                 Pckt:   Pckt No:      Retrn:    Bytes:         Stat:      ");
  762.   x = 48;
  763.   curmode = 1;
  764.   emits (filnam);
  765.   return(0);
  766.   }
  767.  
  768. dostats(type)
  769. char type;
  770.   {
  771.    if (type != 'Y' && type != 'N' && type != 'G') {
  772.       x = 224;
  773.       emit(type);
  774.       x = 312;
  775.       sprintf(snum,"%4d",n+(tp * 64));
  776.       emits(snum);
  777.       if (n==63) tp++;
  778.       x = 408;
  779.       sprintf(snum,"%2d",retry-1);
  780.       emits(snum);
  781.       x = 488;
  782.       sprintf(snum,"%6ld",(long)totbytes);
  783.       emits(snum);
  784.       }
  785.   }
  786.  
  787. /* allow for multi file xfers separated by commas under kermit and XMODEM */
  788.  
  789. multi_xfer(name,mode,do_send)
  790. char *name;
  791. int (*mode)();
  792. int do_send;
  793.     {
  794.     int done = 0;
  795.     int status;
  796.     char *p;
  797.     
  798.     p = name;  
  799.     while(*p && *p != ',')   p++;
  800.     if (*p == '\0') { 
  801.          done = TRUE; 
  802.          if (multi == 1) multi++;
  803.          }
  804.      else multi = 1;
  805.     *p = '\0';
  806.     status = ((*mode)(name, multi));
  807.     if (status == TRUE) {
  808.         if (do_send) emits("Sent File: ");
  809.           else emits("Received File: ");
  810.         emits(name);
  811.         emits("\n");
  812.         emit(8);
  813.         }
  814.     else if (status == FALSE)
  815.         {
  816.         close(fd);
  817.         if (do_send) emits("Send Failed: ");
  818.           else emits("Receive Failed: ");
  819.         emits(name);
  820.         emits("\n");    
  821.         emit(8);
  822.         }    
  823.     if (!done) multi_xfer(++p, mode, do_send);
  824.     server = 0;
  825.     multi = 0;
  826.     }
  827.  
  828. /* gobble up all garb that we received while getting file name strings */
  829.  
  830. eatup()
  831.    {
  832.   while(CheckIO(Read_Request))  {
  833.         WaitIO(Read_Request);
  834.         BeginIO(Read_Request);
  835.         }
  836.    }   
  837.  
  838.